package cc.rengu.igas.channel.wechat.core.packswitch;

import cc.rengu.igas.channel.wechat.common.constant.WechatConfigConstant;
import cc.rengu.igas.channel.wechat.common.constant.WechatParamConstant;
import cc.rengu.igas.channel.wechat.common.constant.WechatTreeNodeConstant;
import cc.rengu.igas.channel.wechat.common.enums.RespCodeEnum;
import cc.rengu.igas.channel.wechat.core.realize.WechatSignService;
import cc.rengu.igas.channel.wechat.core.realize.impl.WechatSignServiceImpl;
import cc.rengu.jradp.packetswitch.IncomingPackSwitchImpl;
import cc.rengu.jradp.packetswitch.OutgoingPackSwitchImpl;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.common.dao.CertInfoMapper;
import cc.rengu.oltp.service.common.dao.SecPlanInfoMapper;
import cc.rengu.oltp.service.common.dao.impl.CertInfoMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.SecPlanInfoMapperImpl;
import cc.rengu.oltp.service.common.entity.CertInfo;
import cc.rengu.oltp.service.common.entity.DstChannelCfg;
import cc.rengu.oltp.service.common.entity.SecPlanInfo;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.utility.util.*;
import cc.rengu.utility.base.StringUtil;
import cc.rengu.utility.cache.UnifiedCache;
import cc.rengu.utility.log.RgLog;
import cc.rengu.utility.log.RgLogger;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import okhttp3.HttpUrl;

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Optional;


/**
 * 与微信通讯层报文解包前后、组前包后实现类
 * 版本：v3
 * Created by 刘玉亮 on 2021/4/2.
 */
public class WechatPaymentClientPackSwitch implements IncomingPackSwitchImpl, OutgoingPackSwitchImpl {
    RgLogger rglog = RgLog.getLogger(this.getClass().getName());

    @Override
    public byte[] beforeInComPackSwitch(byte[] bytes) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String messageBody = new String(bytes, StandardCharsets.UTF_8);
        /* 获取通道安全计划配置 */
        String secPlanInfoListString = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.SEC_PLAN_INFO_LIST);
        List<SecPlanInfo> secPlanInfoList = JSONArray.parseArray(secPlanInfoListString, SecPlanInfo.class);
        Optional<SecPlanInfo> checkSignSecPlanOpt = secPlanInfoList.stream().filter(item -> item.getSecPlanType().equals(WechatParamConstant.VERIFY_CERT_TYPE)).findFirst();
        if (checkSignSecPlanOpt.isPresent()) {
            CertInfoMapper certInfoMapper = new CertInfoMapperImpl();
            CertInfo certInfo = certInfoMapper.selectCertInfoByPrimaryKey(checkSignSecPlanOpt.get().getInstId() + checkSignSecPlanOpt.get().getSecPlanId() + checkSignSecPlanOpt.get().getSecPlanType());
            if (certInfo == null) {
                rglog.error("获取加签证书失败,certIndex:{}", checkSignSecPlanOpt.get().getInstId() + checkSignSecPlanOpt.get().getSecPlanId() + checkSignSecPlanOpt.get().getSecPlanType());
                xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.WX_RETURN_CODE, RespCodeEnum.VERIFY_SIGN_ERROR.getRespCode());
                xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.WX_RETURN_MSG, RespCodeEnum.VERIFY_SIGN_ERROR.getRespDesc());
                return new byte[0];
            }
            /* 生成加签字符串 */
            String nonceStr = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.WX_NONCE);
            String timestamp = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.WX_TIMESTAMP);
            String signBlockString = timestamp + "\n" + nonceStr + "\n" + messageBody + "\n";
            /* 验证签名 */
            String signature = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.WX_SIGNATURE);
            WechatSignService wechatSignService = new WechatSignServiceImpl();
            if (wechatSignService.verifySignString(signature, signBlockString, certInfo)) {
                rglog.info("签名验证成功!");
                return bytes;
            } else {
                rglog.error("签名验证失败!");
                xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.WX_RETURN_CODE, RespCodeEnum.VERIFY_SIGN_ERROR.getRespCode());
                xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.WX_RETURN_MSG, RespCodeEnum.VERIFY_SIGN_ERROR.getRespDesc());
                return new byte[0];
            }
        } else {
            rglog.error("获取验签证书密钥失败,验签失败!");
            xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.WX_RETURN_CODE, RespCodeEnum.VERIFY_SIGN_ERROR.getRespCode());
            xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.WX_RETURN_MSG, RespCodeEnum.VERIFY_SIGN_ERROR.getRespDesc());
            return new byte[0];
        }
    }

    @Override
    public int afterInComPackSwitch() throws Exception {
        return 0;
    }

    @Override
    public int beforeOutgoPackSwitch() throws Exception {
        return 0;
    }

    @Override
    public byte[] afterOutgoPackSwitch(byte[] bytes) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String instId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.INST_ID);
        String messageBody = new String(bytes, StandardCharsets.UTF_8);
        /* 获取通道调用配置信息 */
        DstChannelCfg dstChannelCfg = (DstChannelCfg) xmlTreeUtil.getXmlTreeObjectValue(WechatTreeNodeConstant.DST_CHANNEL_CFG, DstChannelCfg.class);
        /* 根据通道安全计划获取安全计划 */
        SecPlanInfoMapper secPlanInfoMapper = new SecPlanInfoMapperImpl();
        List<SecPlanInfo> secPlanInfoList = secPlanInfoMapper.selectSecPlanInfoBySecPlanId(instId, dstChannelCfg.getSecPlanId());
        if (null != secPlanInfoList && !secPlanInfoList.isEmpty()) {
            xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.SEC_PLAN_INFO_LIST, JSON.toJSONString(secPlanInfoList));
            Optional<SecPlanInfo> secPlanInfoOpt = secPlanInfoList.stream().filter(item -> item.getSecPlanType().equals(WechatParamConstant.SIGN_CERT_TYPE)).findFirst();
            if (secPlanInfoOpt.isPresent()) {
                CertInfoMapper certInfoMapper = new CertInfoMapperImpl();
                CertInfo certInfo = certInfoMapper.selectCertInfoByPrimaryKey(instId + secPlanInfoOpt.get().getSecPlanId() + secPlanInfoOpt.get().getSecPlanType());
                if (certInfo == null) {
                    rglog.error("获取加签证书失败,certIndex:{}", instId + secPlanInfoOpt.get().getSecPlanId() + secPlanInfoOpt.get().getSecPlanType());
                    throw new BizException(RespCodeEnum.GET_THIRD_MCHNT_KEY_ERROR.getRespCode(), RespCodeEnum.GET_THIRD_MCHNT_KEY_ERROR.getRespDesc());
                }
                xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.CERT_ID, certInfo.getCertId());
                xmlTreeUtil.setXmlTreeStringValue(WechatConfigConstant.WECHAT_SIGN_TYPE, certInfo.getAlgorithmType());
                rglog.debug("微信对接配置的签名验签方式为:{}", certInfo.getAlgorithmType());
                /* 生成加签字符串 */
                String nonceStr = RandomUtil.randomString(WechatTreeNodeConstant.WX_RAND_STR_LEN);
                xmlTreeUtil.setXmlTreeStringValue("nonceStr", nonceStr);
                String timestamp = Long.toString(System.currentTimeMillis() / 1000);
                xmlTreeUtil.setXmlTreeStringValue("timestamp", timestamp);
                String signBlockString = generateSignBlockString(nonceStr, timestamp, messageBody);
                rglog.debug("待加签字符串:{}", signBlockString);
                /* 生成签名 */
                WechatSignService wechatSignService = new WechatSignServiceImpl();
                String signature = wechatSignService.generateSignString(signBlockString, certInfo);
                rglog.info("数字签名:<{}>", signature);
                if (signature == null) {
                    rglog.info("生成数字签名失败");
                    throw new BizException(RespCodeEnum.GENERATE_SIGN_ERROR.getRespCode(), RespCodeEnum.GENERATE_SIGN_ERROR.getRespDesc());
                }
                /* 设置https Header */
                xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.DEFINE_HTTP_CONTENT_TYPE, "application/json;charset=utf-8");
                xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.DEFINE_HTTP_ACCEPT, "application/json");
                xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.DEFINE_HTTP_USER_AGENT, "RENGU-IGAS/2.1.0");
                xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.WX_CERT_SERIAL_NO, certInfo.getCertId());
                xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.WX_AUTHORIZATION, "WECHATPAY2-SHA256-RSA2048 mchid=\"" + dstChannelCfg.getProviderMchntNo() + "\",nonce_str=\"" + nonceStr + "\",timestamp=\"" + timestamp + "\",serial_no=\"" + certInfo.getCertId() + "\",signature=\"" + signature + "\"");
                xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.DEFINE_HTTP_ARG_LIST, "Accept:Content-Type:User-Agent:Wechatpay-Serial:Authorization");
            }
        } else {
            rglog.error("获取服务商签名密钥失败!");
            throw new BizException(RespCodeEnum.GET_THIRD_MCHNT_KEY_ERROR.getRespCode(), RespCodeEnum.GET_THIRD_MCHNT_KEY_ERROR.getRespDesc());
        }
        return bytes;
    }

    /**
     * 生成待加签字符串
     *
     * @param nonceStr    随机数
     * @param timestamp   时间戳
     * @param messageBody 报文内容
     * @return 待加签字符串
     * @throws Exception 异常
     */
    private String generateSignBlockString(String nonceStr, String timestamp, String messageBody) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String dstChannelTxn = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TXN_NUM);
        String url = (String) UnifiedCache.get("_JRADP_Urls", dstChannelTxn);
        HttpUrl httpurl = HttpUrl.parse(url);
        if (StringUtil.isNullorEmpty(url) || null == httpurl) {
            rglog.error("找不到交易码<{}>的请求URL", dstChannelTxn);
            throw new BizException(RespCodeEnum.GENERATE_SIGN_ERROR.getRespCode(), RespCodeEnum.GENERATE_SIGN_ERROR.getRespDesc());
        }
        String method = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.DEFINE_HTTP_CALL_METHOD);
        String canonicalUrl = httpurl.encodedPath();
        if (httpurl.encodedQuery() != null) {
            canonicalUrl += "?" + httpurl.encodedQuery();
        }
        return method + "\n" + canonicalUrl + "\n" + timestamp + "\n" + nonceStr + "\n" + messageBody + "\n";
    }
}
